package anonymouschat.service;

import java.util.concurrent.ConcurrentHashMap;

import javax.crypto.SecretKey;

import anonymouschat.utils.Print;

/**
 * Upstream is the path of client towards publication server Downstream is the
 * path of publication server towards client
 * 
 * @author jnfrye
 * 
 */
public class ServerConnectionIdManager
{

    private ConcurrentHashMap<Long, String> up2fulldown = new ConcurrentHashMap<Long, String>();

    private ConcurrentHashMap<String, Long> fulldown2up = new ConcurrentHashMap<String, Long>();

    private ConcurrentHashMap<String, Connection> fullDown2OutputStream = new ConcurrentHashMap<String, Connection>();

    private ConcurrentHashMap<String, Long> fullDown2Down = new ConcurrentHashMap<String, Long>();

    private ConcurrentHashMap<String, SecretKey> fullDown2AESKey = new ConcurrentHashMap<String, SecretKey>();

    private ConcurrentHashMap<Connection, ConcurrentHashMap<Long, Long>> allConnectionsMultiMap = new ConcurrentHashMap<Connection, ConcurrentHashMap<Long, Long>>();

    /**
     * Create a new server connection object
     */
    public ServerConnectionIdManager() {

    }

    /**
     * Add a new association to the manager
     * 
     * @param upid
     *            - upstream id that the server upstream server wants
     * @param downid
     *            - id downstream server has associated with this number
     */
    public void add( Long downid, Long upid, Connection channel ) {
        String fulldown;
        
            fulldown = getFullDown( channel, downid );
            this.fullDown2Down.put( fulldown, downid );
            this.fulldown2up.put( fulldown, upid );
            this.up2fulldown.put( upid, fulldown );
            this.fullDown2OutputStream.put( fulldown, channel );
            if( !this.allConnectionsMultiMap.containsKey( channel ) ) {
                this.allConnectionsMultiMap.put( channel,
                        new ConcurrentHashMap<Long, Long>() );
            }
            this.allConnectionsMultiMap.get( channel ).put( downid, upid );

       
    }

    public String getFullDown( Long upid ) {
        return this.up2fulldown.get( upid );
    }

    public Connection getOutChannel( String fulldown ) {
        return this.fullDown2OutputStream.get( fulldown );
    }

    public Long getDownStreamId( String fulldown ) {

        return this.fullDown2Down.get( fulldown );
    }

    /**
     * A representation of the connection and the id provided by the remote
     * system
     */
    public static String getFullDown( Connection c, Long downid ) {
        if( c == null ) {
            Print.error( "Connection in getFullDown is null" );
            return ":" + downid.hashCode();
        }
        if( downid == null ) {
            Print.error( "downid in getFullDown is null" );
            return c.hashCode() + ":";

        }
        return c.hashCode() + ":" + downid.hashCode();
    }

    public Long getUpstreamId( String fulldown ) {
        return this.fulldown2up.get( fulldown );
    }

    public void setAESKeyForRoute( String fulldown, SecretKey AESkey ) {
        this.fullDown2AESKey.put( fulldown, AESkey );
    }

    public SecretKey getAESKeyForRoute( String fulldown ) {
        return this.fullDown2AESKey.get( fulldown );
    }



    /**
     * Returns a list of sockets, with the psuedo-name mappings used on that socket
     * @return
     */
    public String getConnectionMultiMap() {
        StringBuffer sb = new StringBuffer();
        for( Connection conn : this.allConnectionsMultiMap.keySet()) {
            if( !conn.isOpen() ) {
                // clean it up
                this.allConnectionsMultiMap.remove( conn );
                continue;
            }
            if( sb.length() > 0 ) {
                sb.append( "\n" );
            }
            sb.append( conn.getInfo() + ": {" );
            for( Long downid : this.allConnectionsMultiMap.get( conn ).keySet() ) {
                sb.append( downid + "->"
                        + this.allConnectionsMultiMap.get( conn ).get( downid )
                        + ", " );
            }
            sb.append( "}" );
        }
        String result = new String( sb );
        result = result.replace( ", }", "}" );
        return result;
    }


}
